The highlight of every new version is, of course, the new features. They often help us to simplify our code and program more securely. Version 8.3 also includes a few adjustments that allow PHP to provide us with better error handling and enable us to keep an even closer eye on our code. This article is intended to provide an overview of the most important changes. A complete overview of all major and minor changes can be found in the official release notes [1].
Cloning of readonly classes
You want to clone an object, but instead you only get an error message from PHP. Anyone who uses the readonly properties of PHP 8.1 or the readonly classes of PHP 8.2 may already be familiar with this problem. This behavior has been adjusted in PHP 8.3. In the magic method __clone, readonly properties of an object can now be overwritten (Listing 1) [2].
class PHP { public string $version = '8.3'; } readonly class Foo { public function __construct( public PHP $php ) {} public function __clone(): void { $this->php = clone $this->php; } } $instance = new Foo(new PHP()); $cloned = clone $instance; $cloned->php->version = '8.3';
IPC NEWSLETTER
All news about PHP and web development
Type-safe constants in classes
Constants are a convenient tool for storing and retrieving fixed values. Once defined, they provide a reliable source of consistently identical data, which is not the case in PHP. A child class can overwrite the constant of a parent class. And not only that, since constants could not previously have a type, a string in a parent class could become an array in the child class, for example. This problem has since been addressed in PHP 8.3 and you can define the class constants in a type-safe way (Listing 2) [3].
interface I { const string VERSION = '1.0.0'; } class Foo implements I { const string VERSION = []; } // Fatal error: Cannot use array as value // for class constant Foo::PHP of type string
Dynamic call of class constants
Let’s stick with the topic of class constants, up until now, these could only be called dynamically in a roundabout way. To do this, you had to use the constant() method, as a direct dynamic call was previously not possible here. Luckily, PHP 8.3 has been adapted accordingly. Constants can now be called dynamically with the same syntax as we already know from the dynamic call of class properties. However, this change not only applies to constants, but has also been implemented for the enums introduced in PHP 8.1 (Listing 3) [4].
class Foo { const PHP = 'PHP 8.3'; } $searchableConstant = 'PHP'; var_dump(Foo::{$searchableConstant});
#[\Override] attribute
With the new #[\Override] attribute, child class methods can be marked to emphasize the deliberate overriding of a method of the parent class. Incidentally, this allows errors in the method definition to be intercepted, as PHP 8.3 issues an error if this method doesn’t exist in the parent class. So instead of looking for an error why the method you want to overwrite is not called, for example, because of a typo in the name, PHP now provides error messages to clearly indicate any issues with method definitions. Additionally, if you modify a parent class and inadvertently remove a method that has been overridden by a child class, you will now be notified with an error message (Listing 4) [5].
use PHPUnit\Framework\TestCase; final class MyTest extends TestCase { protected $logFile; protected function setUp(): void { $this->logFile = fopen('/tmp/logfile', 'w'); } #[\Override] protected function taerDown(): void { fclose($this->logFile); unlink('/tmp/logfile'); } } // Fatal error: MyTest::taerDown() has #[\Override] attribute, // but no matching parent method exists
json_validate() function
JSON is the method of choice in many interfaces when it comes to data exchange. So it’s quite surprising that you can’t avoid parsing a JSON string in PHP to validate it and check whether an error has occurred. That’s no longer the case with PHP 8.3, where there is the new json_validate() method to check whether it is valid JSON before further use. So if you are not interested in the content, but only in the fact that the JSON is valid, you have a new method here that also works more efficiently than a json_decode(), which was previously the only way to check [6], [7]:
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true
New Randomizer::getBytesFromString() method
With the random extension introduced in PHP 8.2, PHP has taken a real and important step towards cryptographically correct random methods. PHP 8.3 introduced the new method Randomizer::getBytesFromString(), which is passed a string of arbitrary characters that should make up the randomly generated string (Listing 5) [8], [9].
// A \Random\Engine may be passed for seeding, // the default is the secure engine. $randomizer = new \Random\Randomizer(); $randomDomain = sprintf( "%s.example.com", $randomizer->getBytesFromString( 'abcdefghijklmnopqrstuvwxyz0123456789', 16, ), ); echo $randomDomain;
YOU LOVE PHP?
Explore the PHP Core Track
New Randomizer::getFloat() and Randomizer::nextFloat() methods
In addition to the getBytesFromString() method, the Randomizer class now has two more methods which return a random float. Randomizer::getFloat() returns a random float whose limits can be defined as desired using the parameters $min and $max; a third parameter can be used to specify whether or not the limit values should be included in the pool of expected random numbers. Randomizer::nextFloat(), on the other hand, returns a float between 0 and 1 and is therefore equivalent to Randomizer::getFloat(0,1, \Random\IntervalBoundary::ClosedOpen) (Listing 6) [10], [11].
$randomizer = new \Random\Randomizer(); $temperature = $randomizer->getFloat( -89.2, 56.7, \Random\IntervalBoundary::ClosedClosed, ); $chanceForTrue = 0.1; // Randomizer::nextFloat() is equivalent to // Randomizer::getFloat(0, 1, \Random\IntervalBoundary::ClosedOpen). // The upper bound, i.e. 1, will not be returned. $myBoolean = $randomizer->nextFloat() < $chanceForTrue;
PHP linter with support for multiple files
A practical command on the command line is php -l. This can be used to check any PHP file for syntax errors. With PHP 8.3, it is now possible to validate not just one, but any number of files at once. Not much has changed in terms of the output; for each additional file, an additional line is output to indicate whether the file contains syntax errors or not [12]:
php -l foo.php bar.php No syntax errors detected in foo.php No syntax errors detected in bar.php
New classes, interfaces and functions
Of course, these are not all the changes that PHP 8.3 has to offer, but they are definitely the most important in the daily life of a PHP developer [13]. The DOM classes DOMElement, DOMNode, DOMNameSpaceNode and DOMParentNode have received new additional helper methods to simplify navigation in the DOM of HTML and XML documents. IntlCalendar has received new helpers to set date and time, and IntlGregorianCalendar has received two new methods to create a calendar based on a date and time. With mb_str_pad there is a function that works analogously to str_pad, but supports multibytestrings. To increment and decrement an alphanumeric string, you can use the str_increment and str_decrement functions from PHP 8.3 onwards.
Deprecations and breaking changes
In the latest version of PHP, there are again deprecations that will be removed in later versions, but there are also a few changes that alter the functionality of existing code [14]. Incorrect data when using PHP’s Date/Time extension has previously led to warnings or errors in the form of \Exception or \Error. These were not always easy to handle, as no specific exceptions were thrown. This changes with PHP 8.3, for example, there is now a general DateException for all errors caused by dates that generate an error when parsing. The DateException is implemented in several child exceptions such as the DateInvalidTimeZoneException. When initializing an empty array with a negative index n, PHP 8.3 ensures that the next key is not 0 but n + 1.
IPC NEWSLETTER
All news about PHP and web development
Outlook for PHP 8.4
Of course, the PHP developers aren’t just standing around after version 8.3’s release. The first changes for PHP 8.4 have already been announced [15]. For example, the parsing of HTML5 documents with the DOM extension is to be simplified and there are a few changes to the just-in-time compiler. BCrypt, the hashing algorithm used by PHP to hash passwords, is to become more expensive by making it more difficult to crack passwords. With mb_trim, trim is also finally getting a sister function that can work with multi-byte strings.
Links & Literature
[1] https://www.php.net/releases/8.3/en.php
[2] https://wiki.php.net/rfc/readonly_amendments
[3] https://wiki.php.net/rfc/typed_class_constants
[4] https://wiki.php.net/rfc/dynamic_class_constant_fetch
[5] https://wiki.php.net/rfc/marking_overriden_methods
[6] https://wiki.php.net/rfc/json_validate
[7] https://www.php.net/manual/en/function.json-validate.php
[8] https://wiki.php.net/rfc/randomizer_additions#getbytesfromstring
[9] https://www.php.net/manual/en/random-randomizer.getbytesfromstring.php
[10] https://wiki.php.net/rfc/randomizer_additions#getfloat
[11] https://www.php.net/manual/en/random-randomizer.getfloat.php
[12] https://www.php.net/manual/en/features.commandline.options.php
[13] https://www.php.net/releases/8.3/en.php#other_new_things
[14] https://www.php.net/releases/8.3/en.php#deprecations_and_bc_breaks